"""
Copyright © 2024 Walkline Wang (https://walkline.wang)
Gitee: https://gitee.com/walkline/micropython-ws2812-digital-effect
"""
from random import randint
from time import sleep_ms
from ws2812 import WS2812
from config import Config


class DigitalEffect3(object):
	UP    = 0
	DOWN  = 1
	LEFT  = 2
	RIGHT = 3

	def __init__(self, leds:WS2812):
		self.__width  = Config.Font.WIDTH
		self.__height = Config.Font.HEIGHT

		self.__leds = leds
		self.__last_value = 1

		self.__shift = {
			self.UP   : self.__shift_up,
			self.DOWN : self.__shift_down,
			self.LEFT : self.__shift_left,
			self.RIGHT: self.__shift_right
		}

		self.__append = {
			self.UP   : self.__append_down,
			self.DOWN : self.__append_up,
			self.LEFT : self.__append_right,
			self.RIGHT: self.__append_left
		}

	def show(self, value, x, y, direction=LEFT):
		'''在指定坐标显示数字'''
		if value not in Config.DIGITALS_3.keys():
			return

		self.__current_frame = [[0] * self.__width for _ in range(self.__height)]
		self.__in_frame      = [[0] * self.__width for _ in range(self.__height)]

		in_dots  = f'{Config.DIGITALS_3[value]:0>15b}'
		out_dots = f'{Config.DIGITALS_3[self.__last_value]:0>15b}'

		for row in range(self.__height):
			for col in range(self.__width):
				self.__in_frame[row][col]  = int(in_dots[col * self.__height + row])
				self.__current_frame[row][col] = int(out_dots[col * self.__height + row])

		self.__show_effect(x, y, direction)

		self.__last_value = value

	def __show_effect(self, x, y, direction):
		# 根据矩阵灯珠连接顺序，计算坐标对应的灯珠索引号
		start = x * Config.Matrix.HEIGHT + y

		for count in range(5 if direction in (self.LEFT, self.RIGHT) else 7):
			index = 0

			if count in (0, 1):
				self.__shift[direction]()
			else:
				self.__shift[direction]()
				self.__append[direction](count)

			for dot in self.__get_dots():
				led_index = start + index // self.__height * Config.Matrix.HEIGHT + index % self.__height
				self.__leds.set_pixel(led_index, Config.Colors.BRIGHT if dot == '1' else Config.Colors.BLACK)

				index += 1

			self.__leds.show()
			sleep_ms(80)

	def __get_dots(self):
		value = ''

		for col in range(self.__width):
			for row in range(self.__height):
				value += str(self.__current_frame[row][col])

		return value


	#region matrix data process functions
	def __shift_up(self):
		self.__current_frame.pop(0)
		self.__current_frame.append([0] * self.__width)

	def __shift_down(self):
		self.__current_frame.pop(-1)
		self.__current_frame.insert(0, [0] * self.__width)

	def __shift_left(self):
		for row in self.__current_frame:
			row.pop(0)
			row.append(0)

	def __shift_right(self):
		for row in self.__current_frame:
			row.pop(-1)
			row.insert(0, 0)

	def __append_up(self, times):
		self.__current_frame[0] = self.__in_frame[-times + 1]

	def __append_down(self, times):
		self.__current_frame[-1] = self.__in_frame[times - 2]

	def __append_left(self, times):
		for index, row in enumerate(self.__current_frame):
			row[0] = self.__in_frame[index][-times + 1]

	def __append_right(self, times):
		for index, row in enumerate(self.__current_frame):
			row[-1] = self.__in_frame[index][times - 2]
	#endregion


	@property
	def last_value(self):
		return self.__last_value

	@last_value.setter
	def last_value(self, value):
		self.__last_value = value


def random_generator(max):
	while True:
		yield randint(0, max)

def main():
	leds = WS2812(Config.Matrix.WIDTH, Config.Matrix.HEIGHT, Config.Pin.DIN)

	leds.clean()
	leds.show()

	digital_1 = DigitalEffect3(leds)
	digital_2 = DigitalEffect3(leds)

	numbers    = random_generator(9)
	directions = random_generator(3)

	while True:
		digital_1.show(next(numbers), 1, 0, next(directions))
		sleep_ms(200)

		digital_2.show(next(numbers), 5, 0, next(directions))
		sleep_ms(200)


if __name__ == '__main__':
	main()
